package com.netx.generics.basic;
import com.netx.generics.translation.Results;
import com.netx.generics.translation.TranslationStep;
import java.util.Iterator;
import java.util.ListIterator;
import org.dom4j.Document;
import org.dom4j.Element;


//TODO: before starting to analyze the XML file, we need to guarantee
//that it is correct, with the help of a DTD/Schema validation.
class ContextParser extends TranslationStep {

	private final SymbolRoot _root;
	
	public ContextParser(ContextScanner scanner) {
		super(scanner);
		_root = new SymbolRoot();
	}
	
	public Object performWork(Results r) {
		//TODO parse document version
		Element root = ((Document)r.getContents()).getRootElement();
		_root.sRootCtx = _readContext(r, root, null);
		return _root;
	}

	private SymbolContext _readContext(Results r, Element e, String where) {
		SymbolContext context = new SymbolContext();
		ListIterator<?> itElements = e.elements().listIterator();
		// Root context:
		if(where == null) {
			context.pName = where = "root";
			// Load namespaces:
			while(itElements.hasNext()) {
				Element temp = (Element)itElements.next();
				if(temp.getName().equals("namespace")) {
					SymbolNamespace ns = new SymbolNamespace();
					ns.pPackageName = temp.attributeValue("package");
					ns.pAliasName = temp.attributeValue("alias");
					_root.pNamespaces.add(ns);
				}
				else {
					itElements.previous();
					break;
				}
			}
		}
		else {
			context.pName = e.attributeValue("name");
			where = where+"."+context.pName;
		}
		while(itElements.hasNext()) {
			Element temp = (Element)itElements.next();
			if(temp.getName().equals("property")) {
				SymbolProperty sp = _readProperty(r, temp, where);
				if(sp != null) {
					context.properties.add(sp);
				}
			}
			else if(temp.getName().equals("context")) {
				SymbolContext sc = _readContext(r, temp, where);
				if(sc != null) {
					context.contexts.add(sc);
				}
			}
			else {
				addError(r, where, "illegal element \""+temp.getName()+"\"");
			}
		}
		return context;
	}

	private SymbolProperty _readProperty(Results r, Element e, String where) {
		// Name:
		SymbolProperty sProperty = new SymbolProperty();
		sProperty.pName = e.attributeValue("name");
		where = where+"."+sProperty.pName;
		// class name:
		_parseClassName(r, e.attributeValue("class"), sProperty);
		// direct-value element:
		sProperty.value = _readDirectValue(r, e.attributeValue("value"), where);
		// create / property elements:
		_parseCreateAndProperties(r, e.elements().listIterator(), sProperty, where);
		return sProperty;
	}
	
	private void _parseClassName(Results r, String attribute, SymbolHolder holder) {
		if(attribute == null) {
			attribute = "java.lang.String";
		}
		else {
			attribute = attribute.replaceAll(" ", "");
			if(attribute.endsWith("[]")) {
				holder.pIsArray = true;
			}
		}
		holder.pClassName = attribute;
	}
	
	private SymbolDirectValue _readDirectValue(Results r, String attribute, String where) {
		if(attribute == null) {
			return null;
		}
		else {
			SymbolDirectValue sDirectValue = new SymbolDirectValue();
			String[] array = new String[0];
			// type cast:
			if(attribute.startsWith("(")) {
				if(!attribute.contains(")")) {
					addError(r, where, "missing ')' character");
					return null;
				}
				sDirectValue.typeCast = attribute.substring(1, attribute.indexOf(')'));
				attribute = attribute.substring(attribute.indexOf(')')+1);
			}
			// array:
			// temporarialy eliminate "\;" (this is sleeky):
			String tempString = "_@#$%_";
			attribute = attribute.replaceAll("[\\\\][;]", tempString);
			array = attribute.split("[;]");
			for(String s : array) {
				s.replaceAll(tempString, ";");
			}
			sDirectValue.pValues = array;
			return sDirectValue;
		}
	}
	
	private void _parseCreateAndProperties(Results r, ListIterator<?> it, SymbolHolder holder, String where) {
		if(it.hasNext()) {
			Element create = (Element)it.next();
			if(create.getName().equals("create")) {
				// read all create arguments:
				Iterator<?> itArgs = create.elementIterator();
				if(!itArgs.hasNext()) {
					addError(r, where, "create element must have at least one argument");
				}
				else {
					for(int i=0; itArgs.hasNext(); i++) {
						String where2 = where+".args["+i+"]";
						SymbolArgument sa = _readArgument(r, (Element)itArgs.next(), where2);
						if(sa != null) {
							holder.createArgs.add(sa);
						}
					}
				}
			}
			else {
				it.previous();
			}
		}
		// check for sub-properties:
		while(it.hasNext()) {
			SymbolProperty sp = _readProperty(r, (Element)it.next(), where);
			if(sp != null) {
				holder.properties.add(sp);
			}
		}
	}
	
	private SymbolArgument _readArgument(Results r, Element arg, String where) {
		SymbolArgument sArgument = new SymbolArgument();
		// class name:
		_parseClassName(r, arg.attributeValue("class"), sArgument);
		// direct-value element:
		sArgument.value = _readDirectValue(r, arg.attributeValue("value"), where);
		// create / property elements:
		_parseCreateAndProperties(r, arg.elements().listIterator(), sArgument, where);
		return sArgument;
	}
}
